fix: Spec for list exp and get config endpoints#916
fix: Spec for list exp and get config endpoints#916ayushjain17 wants to merge 3 commits intomainfrom
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis pull request removes the GetConfigFast operation and its models across all SDKs (Haskell, Java, JavaScript, Python, Rust), removes audit_id from GetConfigOutput, and adds If-Modified-Since header support and last_modified/last_modified_at fields to multiple operations. ListExperiment and ListExperimentGroups operations are migrated from GET to POST with request bodies. Frontend and backend changes introduce dimension-based filtering for experiment groups. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90+ minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
56b3166 to
e7359a1
Compare
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
clients/haskell/sdk/SuperpositionSDK.cabal (1)
3-3:⚠️ Potential issue | 🟠 MajorBump package version for this public API change.
Removing modules from
exposed-modulesis a breaking SDK surface change, but Line 3 still keepsversion: 0.0.1. Please increment the package version per your release policy so downstream consumers can detect and adopt the break safely.📦 Suggested update
-version: 0.0.1 +version: 0.1.0Also applies to: 20-323
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/haskell/sdk/SuperpositionSDK.cabal` at line 3, The package still lists "version: 0.0.1" in SuperpositionSDK.cabal despite removing modules from exposed-modules (a breaking public API change); update the version field in SuperpositionSDK.cabal to the next release version appropriate for a breaking change (per your release policy) so downstream consumers can detect the change, e.g., replace "version: 0.0.1" with the new version string.clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs (1)
137-148:⚠️ Potential issue | 🟠 MajorHandle 304 Not Modified responses for conditional requests.
The
If-Modified-Sinceheader enables HTTP conditional requests that return304 Not Modifiedresponses, but the SDK does not handle this status code. When a 304 is received,parseOutputwill reject it (outside the 200-299 range) and return anUnexpectedErrorwith message "Un-expected status code." This breaks the caching semantics of conditional requests.Add a
304 Not Modifiedhandler that returns a cached response, empty/defaultGetConfigOutput, or integrates with the HTTP client's cache headers. Alternatively, remove theIf-Modified-Sinceheader if conditional requests are not intended to be supported.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs` around lines 137 - 148, The request builder for GetConfigInput currently sends an "If-Modified-Since" header (see IntoRequestBuilder/GetConfigInput and intoRequestBuilder calling serHeader "If-Modified-Since") but there is no 304 handling; update the response parsing for this operation (the GetConfigOutput parse function or parseOutput for GetConfigInput/GetConfigOutput) to explicitly handle HTTP 304 by returning an appropriate cached/empty/default GetConfigOutput (or a sentinel indicating "NotModified") instead of treating 304 as an unexpected status, or if conditional requests are not supported, remove the serHeader "If-Modified-Since" from intoRequestBuilder to avoid sending conditional requests altogether. Ensure you reference and update the parse function that currently rejects non-2xx responses so it treats 304 as a valid branch.crates/experimentation_platform/src/api/experiments/handlers.rs (1)
1021-1029:⚠️ Potential issue | 🟠 MajorPOST conditional caching can break due to timestamp format mismatch.
With Line 1036 enabling POST/Smithy flow,
If-Modified-Sinceparsing at Line 1026 still accepts only RFC2822. Smithy clients using RFC3339 won’t hit the 304 path.💡 Suggested fix
let last_modified = req .headers() .get("If-Modified-Since") .and_then(|header_val| header_val.to_str().ok()) .and_then(|header_str| { - DateTime::parse_from_rfc2822(header_str) + DateTime::parse_from_rfc2822(header_str) + .or_else(|_| DateTime::parse_from_rfc3339(header_str)) .map(|datetime| datetime.with_timezone(&Utc)) .ok() });Also applies to: 1036-1043
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/experimentation_platform/src/api/experiments/handlers.rs` around lines 1021 - 1029, The current If-Modified-Since header parsing only tries RFC2822 via DateTime::parse_from_rfc2822, which misses RFC3339 timestamps (used by Smithy/POST clients); update the parsing code that sets last_modified to first attempt parse_from_rfc2822 and if that fails attempt DateTime::parse_from_rfc3339 (converting to Utc) so both formats are accepted, and apply the same change to the equivalent parsing block referenced for the POST/Smithy flow (the code using DateTime::parse_from_rfc2822 and the last_modified variable).smithy/patches/python.patch (1)
10-10:⚠️ Potential issue | 🟡 MinorClarify VERSION provisioning for local development builds.
The CI/CD pipeline in
.github/workflows/release.yamlexplicitly setsVERSIONbefore all Python SDK builds (lines 492, 503, 526), so the release process is not fragile. However, local development builds runninguv buildwithoutVERSIONset will fail. Consider either adding a default fallback version in the Hatch configuration or documenting the requiredVERSIONenvironment variable in the SDK README for developers building locally.Also applies to: 18-20
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@smithy/patches/python.patch` at line 10, The project currently relies on the Hatch dynamic version key dynamic = ["version"] which fails when VERSION is not set for local uv build; fix by providing a safe fallback for VERSION in the Hatch configuration (so dynamic resolution uses a default like a dev/0.0.0 value when the VERSION env var is absent) or alternatively add a clear note in the SDK README that developers must export VERSION before running uv build; update the hatch config entry that references dynamic = ["version"] and/or the README to ensure local builds do not fail.crates/frontend/src/pages/experiment_group_listing/filter.rs (1)
371-378:⚠️ Potential issue | 🟠 MajorReset misses new dimension/context state
Line [375]-[378] resets only classic filters. The newly added context filters remain active because
dimension_params_rws,dimension_buffer_rws, andcontext_rwsare not cleared.Suggested fix
<Button class="h-12 w-48" text="Reset" icon_class="ri-restart-line" on_click=move |event: MouseEvent| { close_drawer("experiment_group_filter_drawer"); event.prevent_default(); batch(|| { filters_rws.set(ExpGroupFilters::default()); + dimension_params_rws.set(DimensionQuery::default()); pagination_params_rws.update(|f| f.reset_page()); filters_buffer_rws.set(ExpGroupFilters::default()); + dimension_buffer_rws.set(DimensionQuery::default()); + context_rws.set(Conditions::default()); }); } />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/frontend/src/pages/experiment_group_listing/filter.rs` around lines 371 - 378, The reset handler currently only clears classic filters (filters_rws, filters_buffer_rws) and resets pagination, but leaves the new context/dimension state intact; update the on_click closure that calls close_drawer(...) so it also clears dimension_params_rws, dimension_buffer_rws, and context_rws (e.g., set them to their defaults or empty state) alongside calling ExpGroupFilters::default() and pagination_params_rws.update(|f| f.reset_page()) to fully reset both classic and new context/dimension filters.
🧹 Nitpick comments (2)
crates/service_utils/src/service/types.rs (1)
41-75: Well-structured header handling centralization.The
AppHeaderimplementation correctly handles date formatting based on client type (RFC3339 for Smithy, RFC2822 for standard HTTP).Minor nit on line 71:
.clone()is unnecessary fori64sinceto_string()takes&self.🔧 Optional: Remove unnecessary clone
pub fn add_config_version( config_version: &Option<i64>, resp_builder: &mut HttpResponseBuilder, ) { if let Some(val) = config_version { resp_builder.insert_header(( Self::XConfigVersion.to_string(), - val.clone().to_string(), + val.to_string(), )); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/service_utils/src/service/types.rs` around lines 41 - 75, In AppHeader::add_config_version the .clone() on the i64 is unnecessary; change the insert_header call to pass val.to_string() (or &val.to_string()) without cloning the i64 so you don't clone the integer, i.e. update the code in add_config_version that references config_version / val and XConfigVersion to remove .clone() before to_string() while keeping the same resp_builder.insert_header(...) usage with HttpResponseBuilder.crates/frontend/src/pages/experiment_group_listing.rs (1)
258-277: Silent error handling may mask API failures.Both
experiment_groups::listanddimensions::listuse.unwrap_or_default()which silently swallows any API errors, returning empty data instead. While this provides resilience for the UI, it could make debugging production issues difficult since users would see empty results with no indication of the underlying failure.Consider logging errors before defaulting, or surfacing a user-visible error state:
let experiment_groups = match experiment_groups::list( &filters, &pagination, &dimension_params, &workspace, &org_id, ).await { Ok(data) => data, Err(e) => { logging::error!("Failed to fetch experiment groups: {:?}", e); PaginatedResponse::default() } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/frontend/src/pages/experiment_group_listing.rs` around lines 258 - 277, The current use of .unwrap_or_default() on experiment_groups::list and dimensions::list silently swallows API errors; update the async block so each call to experiment_groups::list and dimensions::list is handled via match (or ? if the surrounding function returns Result) and on Err(e) call a logging macro (e.g., logging::error!) with a clear message including the error and context (filters/pagination/workspace/org_id), then return a sensible default (e.g., PaginatedResponse::default()) or propagate the error up so the UI can surface it; adjust CombinedResource construction accordingly to use the handled results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs`:
- Line 146: The If-Modified-Since header is being serialized from a UTCTime via
(if_modified_since self) using Io.Superposition.Utility.serHeader, but HTTP
requires RFC7231 format; update the code so the value passed to
Io.Superposition.Utility.serHeader is an HTTPDate (or a formatted RFC7231
string) instead of raw UTCTime: either change the field type to HTTPDate where
GetConfigInput defines if_modified_since or call the existing
formatHTTPDate/HTTPDate conversion function on (if_modified_since self) just
before passing it to Io.Superposition.Utility.serHeader so the header uses
formatHTTPDate output.
In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs`:
- Line 16: GetConfigOutput's audit_id field and setAuditId accessor were
intentionally removed; update any code that expects audit_id on the
GetConfigOutput model (and any callers of setAuditId) to stop accessing or
setting that field. Search for references to GetConfigOutput.audit_id,
setAuditId, or variables named auditId in code paths handling GetConfigOutput
and remove or conditionally gate those usages—if the audit id is still needed
for GetResolvedConfig/GetResolvedConfigWithIdentifier flows, move access to
those models or to the HTTP header handling layer that parses the optional audit
header. Also update affected tests and documentation to reflect that
GetConfigOutput no longer contains audit_id.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs`:
- Line 248: The If-Modified-Since header is being serialized using the UTCTime
JSON SerDe (encodeTrimQuotes/quoteAndDecode) which produces RFC3339 instead of
the required RFC2822 HTTP-date; update the UTCTime SerDe (the SerDe instance for
UTCTime in Utility.hs) or create a header-specific codec so that
Io.Superposition.Utility.serHeader emits HTTP-date formatted timestamps (use
formatTime/parseTimeM with defaultTimeLocale and the RFC2822 pattern or reuse
the existing HTTPDate type/its SerDe), and change the call sites that build the
"If-Modified-Since" header (e.g., the code that calls
Io.Superposition.Utility.serHeader "If-Modified-Since" in
ListExperimentGroupsInput, ListExperimentInput, GetConfigInput) to use this
HTTP-date serializer instead of the JSON encoder.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs`:
- Around line 115-123: The SDK currently hardcodes expectedStatus = status200
for ListExperimentGroups which prevents 304 responses from being treated as a
valid outcome; update the operation's response parsing so that parseOutput (and
the expectedStatus handling) accepts 304 as a valid status and maps it to a
distinct outcome instead of routing to getErrorParser. Concretely, modify the
code that sets expectedStatus for the ListExperimentGroups operation to accept
both status200 and status304 (or add an alternate response constructor) and
update the ListExperimentGroupsOutput parsing logic (the
ListExperimentGroupsOutput constructor/parseOutput path referenced in
Io.Superposition.Model.ListExperimentGroupsOutput and the
parseOutput/getErrorParser flow) to return a 304-specific result (or a union
variant) so callers can detect and handle Not Modified separately. Ensure the
metadata still contains raw status and preserve existing behavior for 200.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs`:
- Around line 115-123: The last-modified header is RFC7231 HTTP-date, so update
the ListExperimentOutput model to use Io.Superposition.Utility.HTTPDate for the
last_modified_at field (instead of Data.Time.UTCTime) and change its
deserialization in the deSerHeader "last-modified" call to produce an HTTPDate
(either by using the utility's HTTP-date-aware header deserializer or by calling
Io.Superposition.Utility.parseHTTPDate on the header string before assigning to
last_modified_at); ensure you update the type reference (last_modified_at) and
the deserialization site (the var0 assignment from deSerHeader "last-modified")
to use Io.Superposition.Utility.HTTPDate/parseHTTPDate so valid RFC7231 dates
parse at runtime.
In `@clients/python/sdk/superposition_sdk/_private/schemas.py`:
- Around line 1720-1728: The timestamp members like if_modified_since that
target DATE_TIME are being serialized as RFC3339 because DATE_TIME is annotated
with `@timestampFormat`("date-time"); update each member-level definition (e.g.,
the if_modified_since member) to add a member-level
`@timestampFormat`("http-date") annotation or change the member target to the
HttpDate shape so the generated client emits RFC2822/http-date for headers like
If-Modified-Since and Last-Modified, then regenerate the Smithy-generated code;
look for other similar members that target DATE_TIME (the other occurrences
flagged in the review) and apply the same per-member
`@timestampFormat`("http-date") fix.
In `@crates/experimentation_platform/src/api/experiment_groups/handlers.rs`:
- Around line 336-352: The cache key used for both the list payload and the
last-modified timestamp causes type confusion because read_through_cache is
storing an Option<DateTime<Utc>> under the same key used to cache
PaginatedResponse<ExperimentGroup>; change the storage so the last-modified
value uses its own distinct key (e.g. suffix like
EXPERIMENT_GROUPS_LAST_MODIFIED_KEY) or always read the timestamp directly from
the DB instead of reusing EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, update locations
using read_through_cache (the call at
read_through_cache::<Option<DateTime<Utc>>> and the later read of the list) to
use the new key or DB fetch, and ensure put_experiment_groups_in_redis
invalidates or sets both keys consistently so deserialization never sees the
wrong payload type.
- Around line 369-375: The shared Redis fast path is incorrectly used for
requests that include context-filtered dimension_params (populated for non-GET
via body.and_then(...).context), causing context-specific subsets to be cached
globally; update the logic that decides to reuse the workspace-wide list cache
(the condition around the Redis fast path at/after where Lines 381-402 are
evaluated) to also require that dimension_params is empty (e.g., check
dimension_params.is_empty()) before using the shared cache so any request with a
non-empty dimension_params bypasses Redis and uses the per-request path.
In `@crates/superposition_sdk/src/protocol_serde/shape_get_config.rs`:
- Around line 80-91: The generated code is using
::aws_smithy_types::date_time::Format::DateTime for the if_modified_since
header; change the Smithy model so the timestamp member "if_modified_since" is
defined with the HTTP-date timestamp format (e.g. set timestampFormat =
"http-date" or the equivalent HttpDate trait on that member) so the generated
code will use ::aws_smithy_types::date_time::Format::HttpDate; update the Smithy
shape for the request/input that contains the if_modified_since member and then
regenerate the Rust code to pick up the new format.
In `@docs/docs/api/Superposition.openapi.json`:
- Around line 172-180: Remove the "If-Modified-Since" header from the POST
operation definitions (the header object named "If-Modified-Since" in
Superposition.openapi.json) and stop documenting 304 responses for those POST
endpoints; instead, move any cache revalidation semantics to the corresponding
GET/HEAD routes or define a POST-specific contract (e.g., explicit conditional
request/response fields) that does not rely on If-Modified-Since/304 per RFC
9110. Ensure all occurrences of the "If-Modified-Since" header and any
referenced 304 response entries in the file (including the other ranges noted)
are deleted or relocated to GET/HEAD operation objects and update the operation
descriptions to reflect the change.
In `@smithy/models/config.smithy`:
- Around line 87-90: Add the `@timestampFormat`("http-date") annotation to the
if_modified_since DateTime header field so it is serialized as an HTTP-date
(RFC2822) rather than RFC3339; update the field declaration for
if_modified_since (the one annotated with `@httpHeader`("If-Modified-Since") and
`@notProperty`) in the models for the GetConfig, ListExperiment, and
ListExperimentGroups operations to include `@timestampFormat`("http-date") so
Python/Rust/JS SDKs emit the correct format.
In `@smithy/models/experiments.smithy`:
- Line 321: The API contract for the ListExperiment operation was changed from
GET /experiments to POST /experiments/list which is a breaking change; update
the service and client surface to avoid breaking consumers by either (a)
restoring/supporting the old GET /experiments endpoint in the backend routing
(and keep the new POST /experiments/list) during a transition, (b) update all
SDKs/clients, docs, and integration tests to use the new POST endpoint and
prepare a major version bump, and/or (c) add explicit migration notices in the
API changelog and README; locate the smithy operation definition for
ListExperiment (the `@http`(method: "POST", uri: "/experiments/list") annotation)
and ensure corresponding server routes, SDK generation, tests, and documentation
are updated to reflect the chosen migration strategy.
---
Outside diff comments:
In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs`:
- Around line 137-148: The request builder for GetConfigInput currently sends an
"If-Modified-Since" header (see IntoRequestBuilder/GetConfigInput and
intoRequestBuilder calling serHeader "If-Modified-Since") but there is no 304
handling; update the response parsing for this operation (the GetConfigOutput
parse function or parseOutput for GetConfigInput/GetConfigOutput) to explicitly
handle HTTP 304 by returning an appropriate cached/empty/default GetConfigOutput
(or a sentinel indicating "NotModified") instead of treating 304 as an
unexpected status, or if conditional requests are not supported, remove the
serHeader "If-Modified-Since" from intoRequestBuilder to avoid sending
conditional requests altogether. Ensure you reference and update the parse
function that currently rejects non-2xx responses so it treats 304 as a valid
branch.
In `@clients/haskell/sdk/SuperpositionSDK.cabal`:
- Line 3: The package still lists "version: 0.0.1" in SuperpositionSDK.cabal
despite removing modules from exposed-modules (a breaking public API change);
update the version field in SuperpositionSDK.cabal to the next release version
appropriate for a breaking change (per your release policy) so downstream
consumers can detect the change, e.g., replace "version: 0.0.1" with the new
version string.
In `@crates/experimentation_platform/src/api/experiments/handlers.rs`:
- Around line 1021-1029: The current If-Modified-Since header parsing only tries
RFC2822 via DateTime::parse_from_rfc2822, which misses RFC3339 timestamps (used
by Smithy/POST clients); update the parsing code that sets last_modified to
first attempt parse_from_rfc2822 and if that fails attempt
DateTime::parse_from_rfc3339 (converting to Utc) so both formats are accepted,
and apply the same change to the equivalent parsing block referenced for the
POST/Smithy flow (the code using DateTime::parse_from_rfc2822 and the
last_modified variable).
In `@crates/frontend/src/pages/experiment_group_listing/filter.rs`:
- Around line 371-378: The reset handler currently only clears classic filters
(filters_rws, filters_buffer_rws) and resets pagination, but leaves the new
context/dimension state intact; update the on_click closure that calls
close_drawer(...) so it also clears dimension_params_rws, dimension_buffer_rws,
and context_rws (e.g., set them to their defaults or empty state) alongside
calling ExpGroupFilters::default() and pagination_params_rws.update(|f|
f.reset_page()) to fully reset both classic and new context/dimension filters.
In `@smithy/patches/python.patch`:
- Line 10: The project currently relies on the Hatch dynamic version key dynamic
= ["version"] which fails when VERSION is not set for local uv build; fix by
providing a safe fallback for VERSION in the Hatch configuration (so dynamic
resolution uses a default like a dev/0.0.0 value when the VERSION env var is
absent) or alternatively add a clear note in the SDK README that developers must
export VERSION before running uv build; update the hatch config entry that
references dynamic = ["version"] and/or the README to ensure local builds do not
fail.
---
Nitpick comments:
In `@crates/frontend/src/pages/experiment_group_listing.rs`:
- Around line 258-277: The current use of .unwrap_or_default() on
experiment_groups::list and dimensions::list silently swallows API errors;
update the async block so each call to experiment_groups::list and
dimensions::list is handled via match (or ? if the surrounding function returns
Result) and on Err(e) call a logging macro (e.g., logging::error!) with a clear
message including the error and context (filters/pagination/workspace/org_id),
then return a sensible default (e.g., PaginatedResponse::default()) or propagate
the error up so the UI can surface it; adjust CombinedResource construction
accordingly to use the handled results.
In `@crates/service_utils/src/service/types.rs`:
- Around line 41-75: In AppHeader::add_config_version the .clone() on the i64 is
unnecessary; change the insert_header call to pass val.to_string() (or
&val.to_string()) without cloning the i64 so you don't clone the integer, i.e.
update the code in add_config_version that references config_version / val and
XConfigVersion to remove .clone() before to_string() while keeping the same
resp_builder.insert_header(...) usage with HttpResponseBuilder.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 77c5dcba-1dc6-42a7-8009-c20b3e5e5c24
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (108)
clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hsclients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hsclients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hsclients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hsclients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hsclients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hsclients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hsclients/haskell/sdk/Io/Superposition/Model/ListExperimentInput.hsclients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hsclients/haskell/sdk/SuperpositionSDK.cabalclients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.javaclients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.javaclients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.javaclients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/Config.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastOutput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroups.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsInput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentInput.javaclients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.javaclients/javascript/sdk/src/Superposition.tsclients/javascript/sdk/src/SuperpositionClient.tsclients/javascript/sdk/src/commands/GetConfigCommand.tsclients/javascript/sdk/src/commands/GetConfigFastCommand.tsclients/javascript/sdk/src/commands/ListExperimentCommand.tsclients/javascript/sdk/src/commands/ListExperimentGroupsCommand.tsclients/javascript/sdk/src/commands/index.tsclients/javascript/sdk/src/models/models_0.tsclients/javascript/sdk/src/protocols/Aws_restJson1.tsclients/python/sdk/superposition_sdk/_private/schemas.pyclients/python/sdk/superposition_sdk/client.pyclients/python/sdk/superposition_sdk/config.pyclients/python/sdk/superposition_sdk/deserialize.pyclients/python/sdk/superposition_sdk/models.pyclients/python/sdk/superposition_sdk/serialize.pycrates/context_aware_config/Cargo.tomlcrates/context_aware_config/src/api/config/handlers.rscrates/context_aware_config/src/api/config/helpers.rscrates/context_aware_config/src/api/context/handlers.rscrates/experimentation_platform/src/api/experiment_groups/handlers.rscrates/experimentation_platform/src/api/experiments/handlers.rscrates/frontend/src/api.rscrates/frontend/src/components/experiment_form.rscrates/frontend/src/pages/experiment_group_listing.rscrates/frontend/src/pages/experiment_group_listing/filter.rscrates/service_utils/src/helpers.rscrates/service_utils/src/redis.rscrates/service_utils/src/service/types.rscrates/superposition/src/resolve/handlers.rscrates/superposition_sdk/src/client.rscrates/superposition_sdk/src/client/customize.rscrates/superposition_sdk/src/client/get_config.rscrates/superposition_sdk/src/client/get_config_fast.rscrates/superposition_sdk/src/client/list_experiment.rscrates/superposition_sdk/src/client/list_experiment_groups.rscrates/superposition_sdk/src/error_meta.rscrates/superposition_sdk/src/operation.rscrates/superposition_sdk/src/operation/get_config/_get_config_input.rscrates/superposition_sdk/src/operation/get_config/_get_config_output.rscrates/superposition_sdk/src/operation/get_config/builders.rscrates/superposition_sdk/src/operation/get_config_fast.rscrates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_input.rscrates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_output.rscrates/superposition_sdk/src/operation/get_config_fast/builders.rscrates/superposition_sdk/src/operation/list_experiment.rscrates/superposition_sdk/src/operation/list_experiment/_list_experiment_input.rscrates/superposition_sdk/src/operation/list_experiment/_list_experiment_output.rscrates/superposition_sdk/src/operation/list_experiment/builders.rscrates/superposition_sdk/src/operation/list_experiment_groups.rscrates/superposition_sdk/src/operation/list_experiment_groups/_list_experiment_groups_input.rscrates/superposition_sdk/src/operation/list_experiment_groups/_list_experiment_groups_output.rscrates/superposition_sdk/src/operation/list_experiment_groups/builders.rscrates/superposition_sdk/src/protocol_serde.rscrates/superposition_sdk/src/protocol_serde/shape_get_config.rscrates/superposition_sdk/src/protocol_serde/shape_get_config_fast.rscrates/superposition_sdk/src/protocol_serde/shape_get_config_fast_output.rscrates/superposition_sdk/src/protocol_serde/shape_get_config_output.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups_input.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups_output.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment_input.rscrates/superposition_sdk/src/protocol_serde/shape_list_experiment_output.rscrates/superposition_sdk/src/serde_util.rscrates/superposition_sdk/src/types.rscrates/superposition_sdk/src/types/builders.rscrates/superposition_types/src/api/experiment_groups.rscrates/superposition_types/src/api/experiments.rscrates/superposition_types/src/database/models/experimentation.rsdocs/docs/api/Superposition.openapi.jsondocs/docs/api/get-config-fast.api.mdxdocs/docs/api/get-config.api.mdxdocs/docs/api/list-experiment-groups.api.mdxdocs/docs/api/list-experiment.api.mdxdocs/docs/api/sidebar.tssmithy/models/config.smithysmithy/models/experiment_groups.smithysmithy/models/experiments.smithysmithy/patches/java.patchsmithy/patches/python.patch
💤 Files with no reviewable changes (30)
- crates/service_utils/src/helpers.rs
- crates/superposition_sdk/src/operation.rs
- clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java
- crates/superposition_sdk/src/client/get_config_fast.rs
- crates/superposition_sdk/src/error_meta.rs
- clients/javascript/sdk/src/SuperpositionClient.ts
- clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hs
- crates/superposition_sdk/src/operation/get_config/_get_config_output.rs
- clients/javascript/sdk/src/commands/index.ts
- crates/superposition_sdk/src/client/customize.rs
- clients/javascript/sdk/src/commands/GetConfigFastCommand.ts
- crates/superposition_sdk/src/client.rs
- clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastOutput.java
- crates/superposition_sdk/src/protocol_serde/shape_get_config_output.rs
- crates/superposition_sdk/src/protocol_serde/shape_get_config_fast_output.rs
- docs/docs/api/get-config-fast.api.mdx
- crates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_input.rs
- clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java
- clients/python/sdk/superposition_sdk/client.py
- clients/javascript/sdk/src/Superposition.ts
- crates/context_aware_config/Cargo.toml
- clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java
- crates/superposition_sdk/src/operation/get_config_fast.rs
- crates/superposition_sdk/src/protocol_serde/shape_get_config_fast.rs
- clients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hs
- clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.java
- clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.java
- crates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_output.rs
- crates/superposition_sdk/src/operation/get_config_fast/builders.rs
- clients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hs
| Io.Superposition.Utility.serQuery "prefix" (prefix self) | ||
| Io.Superposition.Utility.serQuery "version" (version self) | ||
| Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) | ||
| Io.Superposition.Utility.serHeader "If-Modified-Since" (if_modified_since self) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for serHeader implementation to verify UTCTime formatting
ast-grep --pattern $'serHeader $_ $_ = $_'
# Also check if there's any custom UTCTime serialization
rg -n "UTCTime|formatTime|RFC" --type hs -C 3Repository: juspay/superposition
Length of output: 50377
🏁 Script executed:
# Find Utility module files
find . -name "*Utility*" -type f | grep -E "\.hs$"
# Search for serHeader function definition
rg -n "serHeader" --type hs -A 5 -B 2
# Search for any HTTP header formatting functions
rg -n "formatTime|toHTTPDate|RFC.*7231" --type hsRepository: juspay/superposition
Length of output: 50376
🏁 Script executed:
# Find SerializeParameter class and instances
rg -n "class SerializeParameter|instance.*SerializeParameter" --type hs -A 3
# Search for toHttpApiData or similar conversion functions for UTCTime
rg -n "instance.*ToHttpApiData|instance.*SerializeParameter.*UTCTime" --type hs -A 5
# Look at the serParameter function implementation
sed -n '200,260p' clients/haskell/sdk/Io/Superposition/Utility.hsRepository: juspay/superposition
Length of output: 5174
🏁 Script executed:
# Find SerDe typeclass and UTCTime instance
rg -n "class SerDe|instance.*SerDe.*UTCTime" --type hs -A 5
# Search for serializeElement function
rg -n "serializeElement" --type hs -B 2 -A 3
# Look for any UTCTime related serialization
rg -n "UTCTime" clients/haskell/sdk/Io/Superposition/Utility.hs -B 2 -A 2Repository: juspay/superposition
Length of output: 50376
🏁 Script executed:
# Find encodeTrimQuotes implementation
sed -n '131,137p' clients/haskell/sdk/Io/Superposition/Utility.hs
# Look for HTTPDate type definition and any imports related to date formatting
rg -n "encodeTrimQuotes|HTTPDate|formatTime" clients/haskell/sdk/Io/Superposition/Utility.hs -B 2 -A 5Repository: juspay/superposition
Length of output: 2469
🏁 Script executed:
# Find GetConfigInput and check the type of if_modified_since
rg -n "if_modified_since" clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs -B 5 -A 2 | head -40Repository: juspay/superposition
Length of output: 1691
Use HTTPDate or implement RFC 7231 formatting for the If-Modified-Since header.
The if_modified_since field is Data.Time.UTCTime, which serializes to ISO 8601 format (e.g., 2024-03-20T10:30:00Z) via JSON encoding. However, the HTTP If-Modified-Since header requires RFC 7231 format (e.g., Sun, 06 Nov 1994 08:49:37 GMT). The codebase already has an HTTPDate type with proper formatHTTPDate serialization. Either change the field type to HTTPDate or implement custom header serialization for UTCTime that converts to RFC 7231 format.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs` at line 146,
The If-Modified-Since header is being serialized from a UTCTime via
(if_modified_since self) using Io.Superposition.Utility.serHeader, but HTTP
requires RFC7231 format; update the code so the value passed to
Io.Superposition.Utility.serHeader is an HTTPDate (or a formatted RFC7231
string) instead of raw UTCTime: either change the field type to HTTPDate where
GetConfigInput defines if_modified_since or call the existing
formatHTTPDate/HTTPDate conversion function on (if_modified_since self) just
before passing it to Io.Superposition.Utility.serHeader so the header uses
formatHTTPDate output.
| version, | ||
| last_modified, | ||
| audit_id | ||
| last_modified |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if any code depends on audit_id from GetConfigOutput
rg -n 'GetConfigOutput.*audit_id|audit_id.*GetConfigOutput' --type hsRepository: juspay/superposition
Length of output: 46
🏁 Script executed:
# View the GetConfigOutput.hs file to verify the changes
head -100 clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hsRepository: juspay/superposition
Length of output: 3948
🏁 Script executed:
# Check GetResolvedConfigOutput.hs to confirm it still has audit_id
grep -n "audit_id\|setAuditId" clients/haskell/sdk/Io/Superposition/Model/GetResolvedConfigOutput.hs | head -20Repository: juspay/superposition
Length of output: 695
🏁 Script executed:
# Look for Smithy specs related to these models
find . -name "*.smithy" -o -name "*spec*" | grep -i config | head -20Repository: juspay/superposition
Length of output: 131
🏁 Script executed:
# Check git log for context about why this change was made
git log --oneline -20 -- clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs 2>/dev/null | head -10Repository: juspay/superposition
Length of output: 97
🏁 Script executed:
# View the config.smithy file to see the GetConfigOutput and GetResolvedConfigOutput definitions
cat smithy/models/config.smithyRepository: juspay/superposition
Length of output: 7218
🏁 Script executed:
# Check if there are any other references or comments explaining the difference
rg -n "GetConfigOutput|GetResolvedConfigOutput" smithy/ -A 5 -B 5 | head -80Repository: juspay/superposition
Length of output: 46
Breaking change: audit_id field removed from GetConfigOutput.
The audit_id field and setAuditId function have been removed from this model. This is a breaking change for SDK consumers who were accessing the audit_id field from GetConfigOutput. This removal is intentional per the Smithy specification: the GetConfig operation does not include audit_id in its output, whereas GetResolvedConfig and GetResolvedConfigWithIdentifier operations retain it as an optional HTTP header field.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs` at line 16,
GetConfigOutput's audit_id field and setAuditId accessor were intentionally
removed; update any code that expects audit_id on the GetConfigOutput model (and
any callers of setAuditId) to stop accessing or setting that field. Search for
references to GetConfigOutput.audit_id, setAuditId, or variables named auditId
in code paths handling GetConfigOutput and remove or conditionally gate those
usages—if the audit id is still needed for
GetResolvedConfig/GetResolvedConfigWithIdentifier flows, move access to those
models or to the HTTP header handling layer that parses the optional audit
header. Also update affected tests and documentation to reflect that
GetConfigOutput no longer contains audit_id.
| Io.Superposition.Utility.serQuery "sort_on" (sort_on self) | ||
| Io.Superposition.Utility.serQuery "name" (name self) | ||
| Io.Superposition.Utility.serQuery "page" (page self) | ||
| Io.Superposition.Utility.serHeader "If-Modified-Since" (if_modified_since self) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
root_dir="clients/haskell/sdk/Io/Superposition"
utility_file="$(fd 'Utility\.hs$' "$root_dir" | head -n1)"
if [[ -z "${utility_file}" ]]; then
echo "Utility.hs not found under ${root_dir}" >&2
exit 1
fi
echo "== Header/timestamp helpers in ${utility_file} =="
rg -n -C4 '\bserHeader\b|\bdeSerHeader\b|UTCTime|formatTime|parseTime|timestamp|http-date|rfc822|iso8601' "${utility_file}"
echo
echo "== Timestamp-header call sites in the generated SDK =="
rg -n -C2 'If-Modified-Since|last-modified' "${root_dir}"Repository: juspay/superposition
Length of output: 10365
Use the correct HTTP-date format for If-Modified-Since header serialization.
The SerDe UTCTime instance at Utility.hs lines 138-140 uses JSON encoding (encodeTrimQuotes/quoteAndDecode), which produces RFC 3339 format (e.g., "2025-10-21T07:28:00Z"). However, HTTP headers require RFC 2822 format (e.g., "Wed, 21 Oct 2025 07:28:00 GMT"). Line 248 and all If-Modified-Since call sites (ListExperimentInput.hs:319, GetConfigInput.hs:146) will send incorrectly formatted timestamps, causing conditional requests to silently fail even when the server responds correctly.
The codebase defines an HTTPDate type with a SerDe instance (Utility.hs lines 142-145) but still uses JSON encoding. Replace the JSON-based serialization for HTTP timestamp headers with proper RFC 2822 formatting via formatTime/parseTimeM and the defaultTimeLocale constant (already imported at Utility.hs line 53). Use HTTPDate or create a header-specific codec that formats timestamps according to the Smithy HTTP-date specification.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs` at
line 248, The If-Modified-Since header is being serialized using the UTCTime
JSON SerDe (encodeTrimQuotes/quoteAndDecode) which produces RFC3339 instead of
the required RFC2822 HTTP-date; update the UTCTime SerDe (the SerDe instance for
UTCTime in Utility.hs) or create a header-specific codec so that
Io.Superposition.Utility.serHeader emits HTTP-date formatted timestamps (use
formatTime/parseTimeM with defaultTimeLocale and the RFC2822 pattern or reuse
the existing HTTPDate type/its SerDe), and change the call sites that build the
"If-Modified-Since" header (e.g., the code that calls
Io.Superposition.Utility.serHeader "If-Modified-Since" in
ListExperimentGroupsInput, ListExperimentInput, GetConfigInput) to use this
HTTP-date serializer instead of the JSON encoder.
| var0 <- Io.Superposition.Utility.deSerHeader "last-modified" | ||
| var1 <- Io.Superposition.Utility.deSerField "data" | ||
| var2 <- Io.Superposition.Utility.deSerField "total_pages" | ||
| var3 <- Io.Superposition.Utility.deSerField "total_items" | ||
| pure $ ListExperimentGroupsOutput { | ||
| total_pages = var1, | ||
| total_items = var2, | ||
| data' = var0 | ||
| total_pages = var2, | ||
| total_items = var3, | ||
| data' = var1, | ||
| last_modified = var0 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
root_dir="clients/haskell/sdk/Io/Superposition"
utility_file="$(fd 'Utility\.hs$' "$root_dir" | head -n1)"
if [[ -z "${utility_file}" ]]; then
echo "Utility.hs not found under ${root_dir}" >&2
exit 1
fi
echo "== Shared response parsing/status handling =="
rg -n -C4 'FromResponseParser|expectedStatus|responseParser|status304|statusCode|responseStatus|Response|Left|throw' "${utility_file}"
echo
echo "== SDK usages around expectedStatus and conditional requests =="
rg -n -C4 'expectedStatus|status304|If-Modified-Since|Not Modified|responseParser' "${root_dir}"Repository: juspay/superposition
Length of output: 50377
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find and examine ListExperimentGroupsOutput.hs fully
echo "=== ListExperimentGroupsOutput.hs ==="
fd 'ListExperimentGroupsOutput\.hs$' --type f
list_file=$(fd 'ListExperimentGroupsOutput\.hs$' --type f | head -n1)
if [[ -n "$list_file" ]]; then
wc -l "$list_file"
cat -n "$list_file" | tail -n 50
fi
echo
echo "=== GetConfigOutput.hs (conditional request endpoint) ==="
getconfig_file=$(fd 'GetConfigOutput\.hs$' --type f | head -n1)
if [[ -n "$getconfig_file" ]]; then
wc -l "$getconfig_file"
cat -n "$getconfig_file"
fi
echo
echo "=== Checking for 304 / Not Modified handling anywhere ==="
rg -r 'status304|304|NotModified|"304"' clients/haskell --type haskell -iRepository: juspay/superposition
Length of output: 11991
Callers cannot detect or handle 304 Not Modified responses—the conditional request flow is broken.
The expectedStatus = status200 hardcoding on line 113 prevents the SDK from accepting 304 responses. When a 304 is received, parseOutput (Utility.hs:597–605) routes it through getErrorParser, which has no handler for 304, resulting in a generic mkUnexpectedError and no way for callers to distinguish conditional hits from true errors. The Smithy spec (experiment_groups.smithy:241–279) explicitly requires callers to handle 304 separately; this implementation makes that impossible.
The runtime does preserve raw HTTP status in metadata, but the SDK API cannot expose it here since non-200 responses are treated as errors. The architecture needs to either support multiple expectedStatus values per operation or provide an alternate response type for 304, to make the caching pattern functional.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs`
around lines 115 - 123, The SDK currently hardcodes expectedStatus = status200
for ListExperimentGroups which prevents 304 responses from being treated as a
valid outcome; update the operation's response parsing so that parseOutput (and
the expectedStatus handling) accepts 304 as a valid status and maps it to a
distinct outcome instead of routing to getErrorParser. Concretely, modify the
code that sets expectedStatus for the ListExperimentGroups operation to accept
both status200 and status304 (or add an alternate response constructor) and
update the ListExperimentGroupsOutput parsing logic (the
ListExperimentGroupsOutput constructor/parseOutput path referenced in
Io.Superposition.Model.ListExperimentGroupsOutput and the
parseOutput/getErrorParser flow) to return a 304-specific result (or a union
variant) so callers can detect and handle Not Modified separately. Ensure the
metadata still contains raw status and preserve existing behavior for 200.
| var0 <- Io.Superposition.Utility.deSerHeader "last-modified" | ||
| var1 <- Io.Superposition.Utility.deSerField "data" | ||
| var2 <- Io.Superposition.Utility.deSerField "total_pages" | ||
| var3 <- Io.Superposition.Utility.deSerField "total_items" | ||
| pure $ ListExperimentOutput { | ||
| total_pages = var1, | ||
| total_items = var2, | ||
| data' = var0 | ||
| total_pages = var2, | ||
| total_items = var3, | ||
| data' = var1, | ||
| last_modified_at = var0 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Find deSerHeader implementation and type constraints"
rg -n --type=hs -C4 '\bdeSerHeader\b'
echo
echo "2) Inspect timestamp parsing helpers/usages (UTCTime, parseTimeM, HTTP-date, ISO-8601)"
rg -n --type=hs -C3 'UTCTime|parseTimeM|http-date|RFC7231|rfc822|iso8601|last-modified'
echo
echo "3) Compare sibling generated models using last-modified header"
rg -n --type=hs -C3 'deSerHeader "last-modified"|last_modified_at|last_modified'Repository: juspay/superposition
Length of output: 50378
🏁 Script executed:
#!/bin/bash
# Find HTTPDate type definition
echo "=== HTTPDate type definition ==="
rg -n --type=hs -B3 -A8 'newtype\s+HTTPDate|data\s+HTTPDate'
echo
echo "=== quoteAndDecode and eitherDecodeStrict implementation ==="
rg -n --type=hs -B2 -A5 'quoteAndDecode|eitherDecodeStrict' | head -80
echo
echo "=== Check for FromJSON/ToJSON for UTCTime and HTTPDate ==="
rg -n --type=hs 'FromJSON.*UTCTime|FromJSON.*HTTPDate|instance.*UTCTime|instance.*HTTPDate' | head -50
echo
echo "=== Look for models using HTTPDate vs UTCTime for last-modified ==="
rg -n --type=hs 'last.modified.*::.*HTTPDate|last.modified.*::.*UTCTime'
echo
echo "=== Search for working examples of http-date header parsing ==="
rg -n --type=hs -B2 -A2 'HTTPDate' | head -100Repository: juspay/superposition
Length of output: 25865
🏁 Script executed:
#!/bin/bash
# Check HTTPDate and UTCTime FromJSON implementations in detail
echo "=== UTCTime FromJSON instance ==="
rg -n --type=hs -B5 -A10 'instance.*FromJSON.*UTCTime|instance.*ToJSON.*UTCTime'
echo
echo "=== Check ListExperimentOutput complete definition ==="
cat clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs | head -150
echo
echo "=== Check if any models use HTTPDate for headers ==="
rg -n --type=hs 'deSerHeader.*HTTPDate|:: Io.Superposition.Utility.HTTPDate'Repository: juspay/superposition
Length of output: 5518
Use HTTPDate type instead of UTCTime for the last-modified header field.
The last-modified header is in RFC7231 format (HTTP-date), not ISO-8601. The field last_modified_at is currently typed as Data.Time.UTCTime, which uses Aeson's ISO-8601 parser via quoteAndDecode. This will fail at runtime on valid HTTP responses. The correct type HTTPDate exists and has proper RFC7231 parsing via parseHTTPDate, but is unused across all generated models. Change the field type to Io.Superposition.Utility.HTTPDate or convert the header before deserialization.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs` around
lines 115 - 123, The last-modified header is RFC7231 HTTP-date, so update the
ListExperimentOutput model to use Io.Superposition.Utility.HTTPDate for the
last_modified_at field (instead of Data.Time.UTCTime) and change its
deserialization in the deSerHeader "last-modified" call to produce an HTTPDate
(either by using the utility's HTTP-date-aware header deserializer or by calling
Io.Superposition.Utility.parseHTTPDate on the header string before assigning to
last_modified_at); ensure you update the type reference (last_modified_at) and
the deserialization site (the var0 assignment from deSerHeader "last-modified")
to use Io.Superposition.Utility.HTTPDate/parseHTTPDate so valid RFC7231 dates
parse at runtime.
| let dimension_params = if req.method() == actix_web::http::Method::GET { | ||
| dimension_params.into_inner() | ||
| } else { | ||
| body.and_then(|b| b.into_inner().context) | ||
| .map(Into::into) | ||
| .unwrap_or_default() | ||
| }; |
There was a problem hiding this comment.
Exclude context-filtered requests from the shared Redis fast path.
Line 369 can populate dimension_params from body.context for POSTs, but Line 381 ignores that when deciding to reuse the workspace-wide list cache. An all=true request with a non-empty context can therefore cache a context-specific subset and return it to later requests with a different context. The minimal safe fix is to bypass Redis whenever dimension_params is non-empty.
🛠️ Minimal safe fix
- let read_from_redis = pagination_params.all.is_some_and(|e| e) && filters.is_empty();
+ let read_from_redis = pagination_params.all.is_some_and(|e| e)
+ && filters.is_empty()
+ && dimension_params.is_empty();Also applies to: 381-402
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/experimentation_platform/src/api/experiment_groups/handlers.rs` around
lines 369 - 375, The shared Redis fast path is incorrectly used for requests
that include context-filtered dimension_params (populated for non-GET via
body.and_then(...).context), causing context-specific subsets to be cached
globally; update the logic that decides to reuse the workspace-wide list cache
(the condition around the Redis fast path at/after where Lines 381-402 are
evaluated) to also require that dimension_params is empty (e.g., check
dimension_params.is_empty()) before using the shared cache so any request with a
non-empty dimension_params bypasses Redis and uses the per-request path.
| if let ::std::option::Option::Some(inner_5) = &input.if_modified_since { | ||
| let formatted_6 = inner_5.fmt(::aws_smithy_types::date_time::Format::DateTime)?; | ||
| let header_value = formatted_6; | ||
| let header_value: ::http::HeaderValue = header_value.parse().map_err(|err| { | ||
| ::aws_smithy_types::error::operation::BuildError::invalid_field("if_modified_since", format!( | ||
| "`{}` cannot be used as a header value: {}", | ||
| &header_value, | ||
| err | ||
| )) | ||
| })?; | ||
| builder = builder.header("If-Modified-Since", header_value); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does aws_smithy_types Format::DateTime produce HTTP-date format for If-Modified-Since header?
💡 Result:
No. aws_smithy_types::date_time::Format::DateTime formats as RFC 3339 (e.g. 2019-12-16T23:48:18Z), not an HTTP-date. [1]
For an If-Modified-Since header you need an HTTP-date (If-Modified-Since = HTTP-date), so you should use Format::HttpDate (e.g. Mon, 16 Dec 2019 23:48:18 GMT). [1][2]
Example:
use aws_smithy_types::{DateTime, date_time::Format};
let ims = dt.fmt(Format::HttpDate)?; // value for If-Modified-SinceSources: [1] https://docs.rs/aws-smithy-types/latest/aws_smithy_types/date_time/enum.Format.html [2] https://www.ietf.org/ietf-ftp/rfc/rfc7232.txt.pdf
🏁 Script executed:
head -100 crates/superposition_sdk/src/protocol_serde/shape_get_config.rs | tail -30Repository: juspay/superposition
Length of output: 1861
Fix If-Modified-Since header format in the Smithy model.
The code uses Format::DateTime (RFC 3339: 2019-12-16T23:48:18Z), but RFC 7232 requires HTTP-date format for If-Modified-Since headers (Mon, 16 Dec 2019 23:48:18 GMT). Update the Smithy model to use Format::HttpDate instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/superposition_sdk/src/protocol_serde/shape_get_config.rs` around lines
80 - 91, The generated code is using
::aws_smithy_types::date_time::Format::DateTime for the if_modified_since
header; change the Smithy model so the timestamp member "if_modified_since" is
defined with the HTTP-date timestamp format (e.g. set timestampFormat =
"http-date" or the equivalent HttpDate trait on that member) so the generated
code will use ::aws_smithy_types::date_time::Format::HttpDate; update the Smithy
shape for the request/input that contains the if_modified_since member and then
regenerate the Rust code to pick up the new format.
| { | ||
| "name": "If-Modified-Since", | ||
| "in": "header", | ||
| "description": "While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates.", | ||
| "schema": { | ||
| "type": "string", | ||
| "description": "While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates." | ||
| } | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What does RFC 9110 say about using If-Modified-Sinceand returning304 Not ModifiedonPOST requests?
💡 Result:
RFC 9110 restricts If-Modified-Since (IMS) to GET and HEAD:
- IMS only applies to
GET/HEAD: “A recipient MUST ignore theIf-Modified-Sinceheader field … if the request method is neither GET nor HEAD.” Therefore, on aPOSTrequest, IMS is to be ignored. [1] 304 Not Modifiedis for conditionalGET/HEAD: Status304 (Not Modified)“indicates that a conditional GET or HEAD request has been received …” (i.e., it’s defined in terms of conditionalGET/HEAD, notPOST). [2]
So, per RFC 9110, you should not use If-Modified-Since on POST, and you should not return 304 Not Modified to a POST based on If-Modified-Since (since IMS must be ignored on POST, and 304 is defined for conditional GET/HEAD). [1][2]
Sources: RFC 9110 (HTTP Semantics) — If-Modified-Since (§13.1.3) and 304 Not Modified (§15.4.5). [1][2]
Don't advertise If-Modified-Since/304 on these POST operations.
RFC 9110 § 13.1.3 restricts If-Modified-Since to GET and HEAD only—recipients must ignore it on other methods. Similarly, 304 Not Modified (RFC 9110 § 15.4.5) is defined exclusively for conditional GET/HEAD requests. These POST endpoints therefore document behavior that violates HTTP semantics and is not interoperable. Additionally, the spec fails to declare a 304 response for these operations. Move cache revalidation to GET/HEAD routes, or use a different contract for these POST endpoints.
Also applies to: 1925-1931, 1952-1960, 2572-2578, 2599-2606
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/docs/api/Superposition.openapi.json` around lines 172 - 180, Remove the
"If-Modified-Since" header from the POST operation definitions (the header
object named "If-Modified-Since" in Superposition.openapi.json) and stop
documenting 304 responses for those POST endpoints; instead, move any cache
revalidation semantics to the corresponding GET/HEAD routes or define a
POST-specific contract (e.g., explicit conditional request/response fields) that
does not rely on If-Modified-Since/304 per RFC 9110. Ensure all occurrences of
the "If-Modified-Since" header and any referenced 304 response entries in the
file (including the other ranges noted) are deleted or relocated to GET/HEAD
operation objects and update the operation descriptions to reflect the change.
| @documentation("While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates.") | ||
| @httpHeader("If-Modified-Since") | ||
| @notProperty | ||
| if_modified_since: DateTime |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== If-Modified-Since definitions =="
rg -n 'If-Modified-Since|if_modified_since' smithy crates clients | head -n 200
echo
echo "== Backend timestamp parsing for If-Modified-Since =="
rg -n 'parse_from_rfc2822|If-Modified-Since' crates/context_aware_config -C 3
echo
echo "== Generated Rust timestamp formatting =="
rg -n 'fmt\(.*Format::DateTime' crates/superposition_sdk/src/protocol_serde -C 2
echo
echo "== Generated JS timestamp formatting =="
rg -n '__serializeDateTime\(input\[_ims\]' clients/javascript/sdk/src/protocols/Aws_restJson1.ts -C 2Repository: juspay/superposition
Length of output: 25338
🏁 Script executed:
# Check what Format::DateTime produces in aws-smithy
cd crates/superposition_sdk/src/protocol_serde && grep -r "Format::DateTime" --include="*.rs" | head -5
# Find __serializeDateTime in JS SDK
rg -n '__serializeDateTime' clients/javascript/sdk/src/protocols/Aws_restJson1.ts | head -3
# Find serialize_rfc3339 in Python SDK
rg -n 'def serialize_rfc3339|serialize_rfc3339' clients/python/sdk/superposition_sdk/ --include="*.py" -B 2 -A 8 | head -30Repository: juspay/superposition
Length of output: 1601
🏁 Script executed:
# Check AWS Smithy documentation or search for datetime format constants
rg -n 'Format::DateTime|Format::HttpDate|date_time.*Format' --type rust | head -20
# Get context around Python RFC3339 serialization
rg -n 'serialize_rfc3339' clients/python/sdk/superposition_sdk/serialize.py -B 5 -A 5 | head -50Repository: juspay/superposition
Length of output: 7292
🏁 Script executed:
# Check if there are any format trait usages in the smithy models
rg -n '@timestampFormat|Format::HttpDate' smithy/ --include="*.smithy"
# Look for any existing datetime format specs
rg -n 'http-date|rfc3339|rfc2822' smithy/ --include="*.smithy" -iRepository: juspay/superposition
Length of output: 947
🏁 Script executed:
# Search for Format enum definition to understand what DateTime format produces
find . -name "*.rs" -type f -exec grep -l "enum Format\|pub.*DateTime.*=" {} \; 2>/dev/null | head -5
# Check the AWS Smithy types module
rg -n "DateTime|HttpDate|Format" crates/superposition_sdk/src/primitives.rs -A 5 -B 2
# Search for JavaScript serializeDateTime implementation
rg -n "__serializeDateTime|serializeDateTime" clients/ -t ts -A 10 | head -50Repository: juspay/superposition
Length of output: 5115
🏁 Script executed:
# Try to find aws-smithy external dependency info
cat Cargo.lock | grep -A 5 "aws-smithy-types" 2>/dev/null | head -20
# Check if there's documentation about Format::DateTime in comments
rg -n "Format::DateTime|serialize.*DateTime" crates/ -B 3 -A 1 | grep -E "DateTime|Format|http-date|rfc3339|rfc2822" | head -30Repository: juspay/superposition
Length of output: 7697
🏁 Script executed:
# Search more broadly for datetime format hints in the SDK and clients
rg -n "iso.?8601|rfc.?3339|rfc.?2822|http.?date" . -i | grep -v ".git" | head -30Repository: juspay/superposition
Length of output: 2770
🏁 Script executed:
# Check if if_modified_since appears in other operations besides get_config
rg -n "if_modified_since: DateTime" smithy/models/ -B 5 | grep -E "@httpHeader|operation|if_modified_since"
# Verify the Rust SDK uses Format::DateTime (not HttpDate) for these operations
rg -n "if_modified_since" crates/superposition_sdk/src/protocol_serde/ | grep "Format::"
# Check what Format::DateTime produces vs Format::HttpDate in AWS Smithy docs
# by looking at actual usage patterns
rg -n "Format::HttpDate|Format::DateTime" crates/superposition_sdk/ | head -20Repository: juspay/superposition
Length of output: 5549
Add @timestampFormat("http-date") to if_modified_since headers
The If-Modified-Since header must use HTTP-date format (RFC2822) for server parsing, but generated SDKs currently serialize it as RFC3339:
- Python SDK sends RFC3339 (
serialize_rfc3339) but backend parses RFC2822 (DateTime::parse_from_rfc2822) - Rust SDK uses
Format::DateTimeinstead ofFormat::HttpDate - JavaScript SDK uses standard Smithy datetime serialization (RFC3339 by default)
This mismatch breaks conditional GET caching (304 responses). The Smithy models already define @timestampFormat("http-date") for HTTP date fields; apply it here.
Required fix
`@documentation`("While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates.")
+ `@timestampFormat`("http-date")
`@httpHeader`("If-Modified-Since")
`@notProperty`
if_modified_since: DateTimeApply to all three operations: GetConfig, ListExperiment, ListExperimentGroups (smithy/models/config.smithy, experiments.smithy, experiment_groups.smithy).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @documentation("While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates.") | |
| @httpHeader("If-Modified-Since") | |
| @notProperty | |
| if_modified_since: DateTime | |
| `@documentation`("While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates.") | |
| `@timestampFormat`("http-date") | |
| `@httpHeader`("If-Modified-Since") | |
| `@notProperty` | |
| if_modified_since: DateTime |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@smithy/models/config.smithy` around lines 87 - 90, Add the
`@timestampFormat`("http-date") annotation to the if_modified_since DateTime
header field so it is serialized as an HTTP-date (RFC2822) rather than RFC3339;
update the field declaration for if_modified_since (the one annotated with
`@httpHeader`("If-Modified-Since") and `@notProperty`) in the models for the
GetConfig, ListExperiment, and ListExperimentGroups operations to include
`@timestampFormat`("http-date") so Python/Rust/JS SDKs emit the correct format.
e7359a1 to
a704dea
Compare
There was a problem hiding this comment.
Pull request overview
Aligns Smithy models, server handlers, docs, and multi-language SDKs around caching (If-Modified-Since / Last-Modified), context-based filtering for list operations, and the removal of deprecated config fast-path APIs.
Changes:
- Updated Smithy API definitions: list endpoints moved to
POST .../list, addedif-modified-sincerequest header +last-modifiedresponse header, and added request-bodycontextfor dimensional filtering. - Updated backend handlers/utilities to implement 304 behavior and consistent
Last-Modifiedresponse headers. - Regenerated/updated SDKs (Rust/Java/JS/Python/Haskell) + docs + integration tests; removed
GetConfigFastandaudit_idfrom config responses.
Reviewed changes
Copilot reviewed 131 out of 132 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/src/experiments.test.ts | Adds 304 caching test for experiment listing |
| tests/src/experiment_groups.test.ts | Adds 304 caching test for experiment-group listing |
| tests/src/config.test.ts | Adds 304 caching tests + TOML/JSON config format tests |
| smithy/patches/java.patch | Patches generated Java SDK for updated Smithy shapes |
| smithy/models/experiments.smithy | Moves list op to collection ops; POST /list; adds headers + context + last_modified |
| smithy/models/experiment_groups.smithy | Moves list op to collection ops; POST /list; adds headers + context + last_modified |
| smithy/models/config.smithy | Removes GetConfigFast; adds if-modified-since; POST TOML/JSON; removes audit_id from GetConfig |
| docs/docs/api/sidebar.ts | Removes GetConfigFast; updates HTTP method labels (GET→POST) and ordering |
| docs/docs/api/get-config-toml.api.mdx | Updates method to POST + documents if-modified-since |
| docs/docs/api/get-config-json.api.mdx | Updates method to POST + documents if-modified-since |
| docs/docs/api/get-config-fast.api.mdx | Removes deprecated GetConfigFast docs |
| crates/superposition_types/src/database/models/experimentation.rs | Implements Contextual for ExperimentGroup |
| crates/superposition_types/src/api/experiments.rs | Adds request-body type for list context (ExperimentListRequest) |
| crates/superposition_types/src/api/experiment_groups.rs | Adds request-body type for list context (ExpGroupListRequest) + filter field |
| crates/superposition_sdk/src/types/builders.rs | Reorders exports (regen/format) |
| crates/superposition_sdk/src/types.rs | Reorders exports (regen/format) |
| crates/superposition_sdk/src/serde_util.rs | Adds defaults/error-correction for required last_modified outputs |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment_output.rs | Adds header deserializer for last-modified |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment_input.rs | Adds JSON body serializer for list context |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups_output.rs | Adds header deserializer for last-modified |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups_input.rs | Adds JSON body serializer for list context |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment_groups.rs | Switches list-experiment-groups to POST + body + headers |
| crates/superposition_sdk/src/protocol_serde/shape_list_experiment.rs | Switches list-experiment to POST + body + headers |
| crates/superposition_sdk/src/protocol_serde/shape_get_config_toml.rs | Serializes if-modified-since header |
| crates/superposition_sdk/src/protocol_serde/shape_get_config_output.rs | Removes audit_id header deserializer |
| crates/superposition_sdk/src/protocol_serde/shape_get_config_json.rs | Serializes if-modified-since header |
| crates/superposition_sdk/src/protocol_serde/shape_get_config_fast_output.rs | Removes GetConfigFast protocol serde output (deprecated op removed) |
| crates/superposition_sdk/src/protocol_serde/shape_get_config_fast.rs | Removes GetConfigFast protocol serde (deprecated op removed) |
| crates/superposition_sdk/src/protocol_serde/shape_get_config.rs | Adds if-modified-since header serialization; removes audit_id parsing |
| crates/superposition_sdk/src/protocol_serde.rs | Drops GetConfigFast modules; adds list input/output modules |
| crates/superposition_sdk/src/operation/list_experiment_groups/builders.rs | Adds fluent builder fields for if_modified_since + context |
| crates/superposition_sdk/src/operation/list_experiment_groups/_list_experiment_groups_output.rs | Adds required last_modified field to output |
| crates/superposition_sdk/src/operation/list_experiment_groups/_list_experiment_groups_input.rs | Adds if_modified_since + context to input |
| crates/superposition_sdk/src/operation/list_experiment_groups.rs | Switches request to POST /list with JSON body |
| crates/superposition_sdk/src/operation/list_experiment/builders.rs | Adds fluent builder fields for if_modified_since + context |
| crates/superposition_sdk/src/operation/list_experiment/_list_experiment_output.rs | Adds required last_modified field to output |
| crates/superposition_sdk/src/operation/list_experiment/_list_experiment_input.rs | Adds if_modified_since + context to input |
| crates/superposition_sdk/src/operation/list_experiment.rs | Switches request to POST /list with JSON body |
| crates/superposition_sdk/src/operation/get_config_toml/builders.rs | Adds fluent builder support for if_modified_since |
| crates/superposition_sdk/src/operation/get_config_toml/_get_config_toml_input.rs | Adds if_modified_since to input |
| crates/superposition_sdk/src/operation/get_config_toml.rs | Switches GetConfigToml to POST |
| crates/superposition_sdk/src/operation/get_config_json/builders.rs | Adds fluent builder support for if_modified_since |
| crates/superposition_sdk/src/operation/get_config_json/_get_config_json_input.rs | Adds if_modified_since to input |
| crates/superposition_sdk/src/operation/get_config_json.rs | Switches GetConfigJson to POST |
| crates/superposition_sdk/src/operation/get_config_fast/builders.rs | Removes GetConfigFast operation builders |
| crates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_output.rs | Removes GetConfigFast output type |
| crates/superposition_sdk/src/operation/get_config_fast/_get_config_fast_input.rs | Removes GetConfigFast input type |
| crates/superposition_sdk/src/operation/get_config/builders.rs | Adds fluent builder support for if_modified_since |
| crates/superposition_sdk/src/operation/get_config/_get_config_output.rs | Removes audit_id from output |
| crates/superposition_sdk/src/operation/get_config/_get_config_input.rs | Adds if_modified_since to input |
| crates/superposition_sdk/src/operation.rs | Removes GetConfigFast module export |
| crates/superposition_sdk/src/error_meta.rs | Removes GetConfigFast error conversions |
| crates/superposition_sdk/src/client/list_experiment_groups.rs | Documents new input/output fields (if_modified_since, context, last_modified) |
| crates/superposition_sdk/src/client/list_experiment.rs | Documents new input/output fields (if_modified_since, context, last_modified) |
| crates/superposition_sdk/src/client/get_config_toml.rs | Documents if_modified_since |
| crates/superposition_sdk/src/client/get_config_json.rs | Documents if_modified_since |
| crates/superposition_sdk/src/client/get_config_fast.rs | Removes GetConfigFast client method |
| crates/superposition_sdk/src/client/get_config.rs | Documents if_modified_since; removes audit_id mention |
| crates/superposition_sdk/src/client/customize.rs | Formatting cleanup |
| crates/superposition_sdk/src/client.rs | Removes GetConfigFast module include |
| crates/superposition/src/resolve/handlers.rs | Switches header helpers to AppHeader; uses shared is_not_modified |
| crates/service_utils/src/service/types.rs | Introduces AppHeader::{add_last_modified, add_config_version} |
| crates/service_utils/src/redis.rs | Adds EXPERIMENT_GROUPS_LAST_MODIFIED_KEY_SUFFIX constant |
| crates/service_utils/src/helpers.rs | Adds shared is_not_modified helper |
| crates/frontend/src/pages/experiment_group_listing.rs | Adds dimension query support when listing experiment groups |
| crates/frontend/src/components/experiment_form.rs | Passes default dimension query when fetching groups |
| crates/frontend/src/api.rs | Adds dimension params to experiment-groups list query string |
| crates/experimentation_platform/src/api/experiments/handlers.rs | Adds POST /list route; uses request-body context; adds Last-Modified + 304 support |
| crates/experimentation_platform/src/api/experiment_groups/handlers.rs | Adds POST /list route; uses request-body context; adds Last-Modified + 304 support |
| crates/context_aware_config/src/api/context/handlers.rs | Simplifies v2 request detection |
| crates/context_aware_config/src/api/config/helpers.rs | Moves header helpers out; makes POST context optional/defaultable |
| crates/context_aware_config/Cargo.toml | Drops unused actix-http dependency |
| clients/python/sdk/superposition_sdk/deserialize.py | Removes GetConfigFast; adds last-modified parsing for list ops; removes audit_id parsing for GetConfig |
| clients/python/sdk/superposition_sdk/client.py | Removes GetConfigFast operation wiring |
| clients/javascript/sdk/src/models/models_0.ts | Removes GetConfigFast + audit_id; adds new list/config header/body fields |
| clients/javascript/sdk/src/commands/index.ts | Removes GetConfigFast export |
| clients/javascript/sdk/src/commands/ListExperimentGroupsCommand.ts | Updates examples/output fields (context, if_modified_since, last_modified) |
| clients/javascript/sdk/src/commands/ListExperimentCommand.ts | Updates examples/output fields (context, if_modified_since, last_modified) |
| clients/javascript/sdk/src/commands/GetConfigTomlCommand.ts | Adds if_modified_since to example |
| clients/javascript/sdk/src/commands/GetConfigJsonCommand.ts | Adds if_modified_since to example |
| clients/javascript/sdk/src/commands/GetConfigFastCommand.ts | Removes GetConfigFast command |
| clients/javascript/sdk/src/commands/GetConfigCommand.ts | Adds if_modified_since to example; removes audit_id mention |
| clients/javascript/sdk/src/SuperpositionClient.ts | Removes GetConfigFast from service input/output unions |
| clients/javascript/sdk/src/Superposition.ts | Removes GetConfigFast command binding + method |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.java | Adds required last-modified header member |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.java | Adds required last-modified header member |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroups.java | Changes to POST /experiment-groups/list |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.java | Changes to POST /experiments/list |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigTomlInput.java | Adds if-modified-since header |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigToml.java | Switches to POST |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.java | Removes audit_id member |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJsonInput.java | Adds if-modified-since header |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJson.java | Switches to POST |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.java | Adds if-modified-since header |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.java | Removes deprecated GetConfigFast input |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.java | Removes deprecated GetConfigFast operation |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java | Updates resource shape after list→collectionOperations move |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.java | Updates resource shape after list→collectionOperations move |
| clients/java/sdk/src/main/java/io/juspay/superposition/model/Config.java | Removes GetConfigFast from resource operations |
| clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java | Removes GetConfigFast client method |
| clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java | Removes GetConfigFast from interface |
| clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java | Removes GetConfigFast async method |
| clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java | Removes GetConfigFast from async interface |
| clients/haskell/sdk/SuperpositionSDK.cabal | Removes GetConfigFast modules from cabal exports |
| clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs | Adds last-modified header parsing + required field |
| clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs | Adds last-modified header parsing + required field |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigTomlInput.hs | Switches to POST; adds if-modified-since header |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs | Removes audit_id parsing |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigJsonInput.hs | Switches to POST; adds if-modified-since header |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs | Adds if-modified-since header |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hs | Removes GetConfigFast output type |
| clients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hs | Removes GetConfigFast input type |
| clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hs | Removes GetConfigFast command |
| Cargo.lock | Removes actix-http from context_aware_config deps |
Comments suppressed due to low confidence (1)
clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java:97
Experiments.collectionOperations()/$COLLECTION_OPERATIONSdoesn't include the actual collection operations (ListExperiment,ApplicableVariants) after the Smithy model change, and instead duplicates$OPERATIONS. This makes the generated resource schema inconsistent with the Smithy resource definition and can break clients that rely on resource metadata.
private static final List<Schema>$COLLECTION_OPERATIONS = List.of(UpdateOverridesExperiment.$SCHEMA,
ConcludeExperiment.$SCHEMA,
DiscardExperiment.$SCHEMA,
RampExperiment.$SCHEMA,
PauseExperiment.$SCHEMA,
ResumeExperiment.$SCHEMA);
private static final List<Schema> $OPERATIONS = List.of(UpdateOverridesExperiment.$SCHEMA,
ConcludeExperiment.$SCHEMA,
DiscardExperiment.$SCHEMA,
RampExperiment.$SCHEMA,
PauseExperiment.$SCHEMA,
ResumeExperiment.$SCHEMA);
private static final Schema $SCHEMA = Schema.createResource($ID);
/**
* Get an instance of this {@code ApiResource}.
*
* @return An instance of this class.
*/
public static Experiments instance() {
return $INSTANCE;
}
private Experiments() {}
@Override
public Schema schema() {
return $SCHEMA;
}
@Override
public Map<String, Schema> identifiers() {
return $IDENTIFIERS;
}
@Override
public Map<String, Schema> properties() {
return $PROPERTIES;
}
@Override
public Schema create() {
return CreateExperiment.$SCHEMA;
}
@Override
public Schema read() {
return GetExperiment.$SCHEMA;
}
@Override
public List<Schema> collectionOperations() {
return $COLLECTION_OPERATIONS;
}
@Override
public List<Schema> operations() {
return $OPERATIONS;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| test("list experiment groups with last_modified_after filter should return correct results", async () => { | ||
| if (!lastModifiedTime) { | ||
| throw new Error( | ||
| "Last modified time not set from previous test, cannot test if-modified-since functionality.", | ||
| ); | ||
| } | ||
| const cmd = new ListExperimentGroupsCommand({ | ||
| workspace_id: ENV.workspace_id, | ||
| org_id: ENV.org_id, | ||
| if_modified_since: lastModifiedTime, | ||
| }); | ||
| try { |
| @@ -1234,6 +1235,7 @@ describe("Experiments API", () => { | |||
| const exp2 = out.data?.find((exp) => exp.id === experimentId2); | |||
| expect(exp1?.experiment_group_id).toBeUndefined(); | |||
| expect(exp2?.experiment_group_id).toBeUndefined(); | |||
| lastModifiedTime = out.last_modified; | |||
| } catch (e: any) { | |||
| console.error( | |||
| "Error in test '9. List Experiments (Basic)':", | |||
| @@ -1242,4 +1244,28 @@ describe("Experiments API", () => { | |||
| throw e; | |||
| } | |||
| }); | |||
|
|
|||
| test("9.1. List Experiments with if-modified-since header", async () => { | |||
| if (!lastModifiedTime) { | |||
| throw new Error( | |||
| "Last modified time not set from previous test, cannot test if-modified-since functionality.", | |||
| ); | |||
| } | |||
| log::info!("last modified {last_modified:?}"); | ||
| let parsed_max: Option<DateTime<Utc>> = max_created_at.and_then(nanosecond_erasure); | ||
| max_created_at.is_some() && parsed_max <= last_modified |
| let max_event_timestamp = read_through_cache::<Option<DateTime<Utc>>>( | ||
| format!( | ||
| "{}{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX}", | ||
| *workspace_context.schema_name | ||
| ), | ||
| &workspace_context.schema_name, |
| ); | ||
| let read_from_redis = pagination_params.all.is_some_and(|e| e) && filters.is_empty(); | ||
|
|
| @@ -85,10 +87,9 @@ public Schema delete() { | |||
| } | |||
|
|
|||
| @Override | |||
| public Schema list() { | |||
| return ListExperimentGroups.$SCHEMA; | |||
| public List<Schema> collectionOperations() { | |||
| return $COLLECTION_OPERATIONS; | |||
| } | |||
735cb33 to
8fc501b
Compare
870f5d1 to
8b9e0a1
Compare
8b9e0a1 to
96105a6
Compare
Problem
Describe the problem you are trying to solve here
Solution
Provide a brief summary of your solution so that reviewers can understand your code
Environment variable changes
What ENVs need to be added or changed
Pre-deployment activity
Things needed to be done before deploying this change (if any)
Post-deployment activity
Things needed to be done after deploying this change (if any)
API changes
Possible Issues in the future
Describe any possible issues that could occur because of this change
Summary by CodeRabbit
New Features
Bug Fixes / Improvements
Removals